use std::any::{Any, TypeId};
use std::collections::HashMap;
use serde::de::DeserializeOwned;
use crate::codec::{RpcCodec, RpcCodecFactory, RpcCodecHeader};
use crate::{RpcContext, RpcResult};
use crate::codec::binary_codec::RpcBinCodeCodec;
use crate::codec::json_codec::RpcJsonCodec;
use crate::dto::{RpcData, RpcHeader};
use crate::error::{CustomerErrorKind, RpcError};

#[macro_export]
macro_rules! codec_convert {
    ($codec: ident, $value: expr) => {
        if $value.is::<$codec>() {
            let result: Box<dyn RpcCodec<T>> = $codec::create();
            Some(result)
        } else {
           None
        }
    };
}

#[macro_export]
macro_rules! codec_convert_header {
    ($codec: ident, $value: expr) => {
        if $value.is::<$codec>() {
            let result: Box<dyn RpcCodecHeader> = Box::new($codec::default());
            Some(result)
        } else {
           None
        }
    };
}

pub(crate) fn get_current_rpc_header_codec(rpc_content: &RpcContext) -> RpcResult<Box<dyn RpcCodecHeader>> {
    let rpc_codec_map = rpc_content.get_current_rpc_codec_gather();
    let codec_type = rpc_content.get_current_codec_type();
    if let Some(codec_type) = &codec_type {
        let any = rpc_codec_map.get_codec_factory(codec_type);
        if let Some(any) = any {
            if let Some(codec) = codec_convert_header!(RpcJsonCodec, any) {
                return Ok(codec);
            }

            if let Some(codec) = codec_convert_header!(RpcBinCodeCodec, any) {
                return Ok(codec);
            }
        }
    }

    Err(RpcError::CustomerError {error_kid: CustomerErrorKind::CodecTypeNoSetError, message: "未设置编码类型或设置编码集".to_string() })
}

pub(crate) fn get_current_rpc_codec<T: bincode::Decode<()> + DeserializeOwned + serde::Serialize + bincode::Encode>(rpc_content: &RpcContext) -> RpcResult<Box<dyn RpcCodec<T>>> {
    let rpc_codec_map = rpc_content.get_current_rpc_codec_gather();
    let codec_type = rpc_content.get_current_codec_type();
    if let Some(codec_type) = &codec_type {
        let any = rpc_codec_map.get_codec_factory(&codec_type);
        if let Some(any) = any {
            if let Some(codec) = codec_convert!(RpcBinCodeCodec, any) {
                return Ok(codec);
            }

            if let Some(codec) = codec_convert!(RpcJsonCodec, any) {
                return Ok(codec);
            }
        }
    }
    Err(RpcError::CustomerError {error_kid: CustomerErrorKind::CodecTypeNoSetError, message: "未设置编码类型或设置编码集".to_string() })
}


pub struct RpcCodecRegistry {
    header_codecs: HashMap<TypeId, fn(&dyn Any) -> Option<&dyn RpcCodecHeader>>,
}

impl RpcCodecRegistry {
    pub fn new() -> RpcCodecRegistry {
        RpcCodecRegistry {
            header_codecs: HashMap::new(),
        }
    }

    pub fn register_header<T: RpcCodecHeader + Any>(&mut self) {
        self.header_codecs.insert(
            TypeId::of::<T>(),
            |any| any.downcast_ref::<T>().map(|t| t as &dyn RpcCodecHeader)
        );
    }

    pub fn get_header_codec<'a>(&self, any: &'a dyn Any) -> Option<&'a dyn RpcCodecHeader> {
        let type_id = any.type_id();
        if let Some(&converter) = self.header_codecs.get(&type_id) {
            converter(any)
        } else {
            None
        }
    }
}

pub struct RpcDefaultCodec;

impl RpcCodecHeader for RpcDefaultCodec {
    fn encode_header(&self, _header: &RpcHeader) -> RpcResult<Vec<u8>> {
        todo!()
        // if codec_type == "json" {
        //     let mut json_codec = RpcJsonCodec::new();
        //
        //     return json_codec.to_bytes();
        // } else if codec_type == "bin-code" {
        //
        // }
    }

    fn decode_header(&self, _data: &Vec<u8>) -> RpcResult<(RpcHeader, Vec<u8>)> {
        todo!()
    }

    fn encode_data(&self, _header: &RpcHeader, _data: &Vec<u8>) -> RpcResult<Vec<u8>> {
        todo!()
    }
}

impl <T> RpcCodec<T> for RpcDefaultCodec {
    fn encode(&self, _data: &T) -> RpcResult<Vec<u8>> {
        todo!()
    }

    fn decode(&self, _data: Vec<u8>) -> RpcResult<T> {
        todo!()
    }

    fn encode_rpc_data(&self, _data: &RpcData<T>) -> RpcResult<Vec<u8>> {
        todo!()
    }
}